package com.example.demo.generator.scaffold.util;

import com.example.demo.generator.scaffold.model.GeneratedFile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 将生成文件写入临时目录并打包为 ZIP。
 */
@Service
public class ZipPackagingService {

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    private final Path baseOutputDir;
    private final Duration ttl;
    private final ConcurrentLinkedQueue<ZipResult> results = new ConcurrentLinkedQueue<>();

    public ZipPackagingService(
            @Value("${scaffold.output.base:#{null}}") String baseDir,
            @Value("${scaffold.output.ttl-minutes:60}") long ttlMinutes
    ) throws IOException {
        if (baseDir == null || baseDir.isBlank()) {
            this.baseOutputDir = Files.createTempDirectory("blockasset-scaffold");
        } else {
            this.baseOutputDir = Files.createDirectories(Path.of(baseDir));
        }
        this.ttl = Duration.ofMinutes(ttlMinutes);
    }

    public ZipResult generateZip(List<GeneratedFile> files, String downloadPrefix) throws IOException {
        if (files == null || files.isEmpty()) {
            throw new IllegalArgumentException("No files to package");
        }

        Path tempDir = Files.createTempDirectory(baseOutputDir, "workspace-");
        for (GeneratedFile file : files) {
            Path target = tempDir.resolve(file.getRelativePath());
            Files.createDirectories(target.getParent());
            Files.writeString(target, file.getContent(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
        }

        String zipName = "blockasset-output-" + FORMATTER.format(LocalDateTime.now()) + ".zip";
        Path zipPath = baseOutputDir.resolve(zipName);
        try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
            for (GeneratedFile file : files) {
                Path filePath = tempDir.resolve(file.getRelativePath());
                ZipEntry entry = new ZipEntry(file.getRelativePath());
                zos.putNextEntry(entry);
                byte[] bytes = Files.readAllBytes(filePath);
                zos.write(bytes);
                zos.closeEntry();
            }
        }
        ZipResult result = new ZipResult(tempDir, zipPath, LocalDateTime.now(), downloadPrefix + zipPath.getFileName().toString());
        results.offer(result);
        return result;
    }

    public Path resolveZipPath(String fileName) {
        return baseOutputDir.resolve(fileName);
    }

    public static class ZipResult {
        private final Path workspace;
        private final Path zipFile;
        private final LocalDateTime createdAt;
        private final String downloadUrl;

        public ZipResult(Path workspace, Path zipFile, LocalDateTime createdAt, String downloadUrl) {
            this.workspace = workspace;
            this.zipFile = zipFile;
            this.createdAt = createdAt;
            this.downloadUrl = downloadUrl;
        }

        public Path getWorkspace() {
            return workspace;
        }

        public Path getZipFile() {
            return zipFile;
        }

        public LocalDateTime getCreatedAt() {
            return createdAt;
        }

        public String getDownloadUrl() {
            return downloadUrl;
        }
    }

    @Scheduled(cron = "0 */10 * * * *")
    public void cleanUp() {
        LocalDateTime now = LocalDateTime.now();
        List<ZipResult> toRemove = new ArrayList<>();

        // 遍历查找过期的结果
        for (ZipResult result : results) {
            if (Duration.between(result.getCreatedAt(), now).compareTo(ttl) > 0) {
                toRemove.add(result);
            }
        }

        // 逐个移除，避免 ConcurrentLinkedQueue.removeAll 抛出 UnsupportedOperationException
        toRemove.forEach(result -> {
            results.remove(result);
            deleteResult(result);
        });
    }

    private void deleteResult(ZipResult result) {
        try {
            if (Files.exists(result.getZipFile())) {
                Files.delete(result.getZipFile());
            }
            if (Files.exists(result.getWorkspace())) {
                Files.walk(result.getWorkspace())
                        .sorted((a, b) -> b.compareTo(a))
                        .forEach(path -> {
                            try {
                                Files.deleteIfExists(path);
                            } catch (IOException ignored) {
                            }
                        });
            }
        } catch (IOException ignored) {
        }
    }
}
